package jmeter.netty;

import java.util.concurrent.CyclicBarrier;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import bot.Head;
import bot.Leg;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.util.concurrent.GlobalEventExecutor;

public class EchoNettyServer {
	protected static final Logger LOCAL_LOG = LogManager
			.getLogger(LogManager.ROOT_LOGGER_NAME);

	private int port;

	public EchoNettyServer(int port) {
		this.port = port;
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {

		int port;
		if (args.length > 0) {
			port = Integer.parseInt(args[0]);
		} else {
			port = 12345;
		}
		try {
			new EchoNettyServer(port).run();
		} catch (InterruptedException e) {
			LOCAL_LOG.error(e.toString(), e);
		}
	}

	public void run() throws InterruptedException {
		EventLoopGroup bossGroup = new NioEventLoopGroup();
		EventLoopGroup workerGroup = new NioEventLoopGroup();
		try {
			ServerBootstrap bootstrap = new ServerBootstrap();
			bootstrap
					.group(bossGroup, workerGroup)
					.channel(NioServerSocketChannel.class)
					.childHandler(new ChannelInitializer<SocketChannel>() {
						@Override
						public void initChannel(SocketChannel ch)
								throws Exception {
							ch.pipeline().addLast(new EchoServerHandler());
						}
					}).option(ChannelOption.SO_BACKLOG, 128)
					.childOption(ChannelOption.SO_KEEPALIVE, true);

			// Bind and start to accept incoming connections.
			ChannelFuture f = bootstrap.bind(port).sync(); // (7)

			// Wait until the server socket is closed.
			// In this example, this does not happen, but you can do that to
			// gracefully
			// shut down your server.
			f.channel().closeFuture().sync();
		} finally {
			workerGroup.shutdownGracefully();
			bossGroup.shutdownGracefully();
		}
	}

	class EchoServerHandler extends ChannelInboundHandlerAdapter {

		final ChannelGroup channels = new DefaultChannelGroup(
				GlobalEventExecutor.INSTANCE);

		@Override
		public void channelActive(ChannelHandlerContext ctx) throws Exception {
			channels.add(ctx.channel());
		}

		@Override
		public void channelRead(ChannelHandlerContext ctx, Object msg)
				throws Exception {

			ctx.write(msg);
			channels.flushAndWrite(msg);
		}

		@Override
		public void channelReadComplete(ChannelHandlerContext ctx)
				throws Exception {
			ctx.flush();
		}

		@Override
		public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
			LOCAL_LOG.error(cause.getMessage(), cause);
			ctx.close();
		}

	}
}
